#include "pch.hpp"
#include "Camera.hpp"

using namespace Engine;
using namespace Engine::Video;




Camera::Camera(const EngineInfo& info)
	: m_position(0, 0, 0)
	, m_lookAt(1, 0, 0)
	, m_fNearPlane(0)
	, m_fFarPlane(1)
	, m_fAspect(info.displayMode.aspectRatio())
	, m_fFOV(info.fov)
{
	m_viewProjectionInverse.identity();
	m_viewProjection.identity();
	m_projection.identity();
	m_view.identity();

	m_previousViewProjectionInverse.identity();
	m_previousViewProjection.identity();
	m_previousProjection.identity();
	m_previousView.identity();

	resize(info.displayMode.width, info.displayMode.height);
}
///////////////////////////////////////////////////////////////////////////////////////////////////

Camera::~Camera()
{}
///////////////////////////////////////////////////////////////////////////////////////////////////




float3 Camera::position() const
{
	return m_position;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void Camera::setPosition(float3 pos)
{
	m_position = pos;
}
///////////////////////////////////////////////////////////////////////////////////////////////////




float3 Camera::lookAt() const
{
	return m_lookAt;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void Camera::setLookAt(float3 pos)
{
	m_lookAt = pos;
}
///////////////////////////////////////////////////////////////////////////////////////////////////




float2 Camera::clippingPlanes() const
{
	return float2(m_fNearPlane, m_fFarPlane);
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void Camera::setClippingPlanes(const float2& planes)
{
	m_fNearPlane = planes.x;
	m_fFarPlane  = planes.y;
}
///////////////////////////////////////////////////////////////////////////////////////////////////




float4x4 Camera::view() const
{
	return m_view;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

float4x4 Camera::projection() const
{
	return m_projection;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

float4x4 Camera::previousView() const
{
	return m_previousView;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

float4x4 Camera::previousProjection() const
{
	return m_previousProjection;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

float4x4 Camera::viewProjection() const
{
	return m_viewProjection;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

float4x4 Camera::previousViewProjection() const
{
	return m_previousViewProjection;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

float4x4 Camera::viewProjectionInverse() const
{
	return m_viewProjectionInverse;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

float4x4 Camera::previousViewProjectionInverse() const
{
	return m_previousViewProjectionInverse;
}
///////////////////////////////////////////////////////////////////////////////////////////////////




float3 Camera::project(const float3& worldPoint) const
{
	return math::transform(m_viewProjection, worldPoint);
}
///////////////////////////////////////////////////////////////////////////////////////////////////

float3 Camera::unproject(const float3& screenPoint) const
{
	float4 worldPoint;

	worldPoint = math::transform(m_viewProjectionInverse, float4(screenPoint, 1));
	return worldPoint.xyz() / worldPoint.w;
}
///////////////////////////////////////////////////////////////////////////////////////////////////




bool Camera::isValid(const float2& screenPoint) const
{
	return screenPoint.x >= -1 && screenPoint.x <= 1 &&
		screenPoint.y >= -1 && screenPoint.y <= 1;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

bool Camera::isValid(const float3& screenPoint) const
{
	return screenPoint.x >= -1 && screenPoint.x <= 1 &&
		screenPoint.y >= -1 && screenPoint.y <= 1 &&
		screenPoint.z >= 0  && screenPoint.z <= 1;
}
///////////////////////////////////////////////////////////////////////////////////////////////////




void Camera::resize(uint32 screenWidth, uint32 screenHeight)
{
	m_fAspect = 1.0f * screenWidth / screenHeight;

	m_projection = float4x4::perspectiveLH(angle::degrees(m_fFOV),
		m_fAspect,
		m_fNearPlane,
		m_fFarPlane);
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void Camera::calculateViewMatrix()
{
	float3 lookDir = m_lookAt - m_position;
	m_view = float4x4::lookAtLH(m_position, lookDir);

	m_viewProjection = m_view * m_projection;
	m_viewProjectionInverse = inverse(m_viewProjection);
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void Camera::update()
{
	m_previousView                  = m_view;
	m_previousProjection            = m_projection;
	m_previousViewProjection        = m_viewProjection;
	m_previousViewProjectionInverse = m_viewProjectionInverse;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
